#ifndef CLOSURE_H
#define CLOSURE_H

#include "base/macro_def.h"

class Closure {
  public:
    virtual ~Closure() {
    }

    virtual void Run() = 0;

  protected:
    Closure() {
    }

  private:
    DISALLOW_COPY_AND_ASSIGN(Closure);
};

class StopableClosure : public Closure {
  public:
    explicit StopableClosure(Closure* c)
        : closure_(c), exit_(false) {
    }

    void Run() {
      while (!exit_) {
        closure_->Run();
      }
    }

    void Stop() {
      exit_ = true;
    }

  private:
    scoped_ptr<Closure> closure_;
    bool exit_;

    DISALLOW_COPY_AND_ASSIGN(StopableClosure);
};

class FuncClosure0 : public Closure {
  public:
    typedef void (*Func)();
    explicit FuncClosure0(Func f, bool self_del = false)
        : f_(f), self_del_(self_del) {
      CHECK_NOTNULL(f_);
    }

    ~FuncClosure0() {
      if (self_del_) {
        delete this;
      }
    }

    void Run() {
      if (f_ != NULL) {
        f_();
      }
    }

  private:
    Func f_;
    bool self_del_;

    DISALLOW_COPY_AND_ASSIGN(FuncClosure0);
};

template<typename Type>
class FuncClosure1 : public Closure {
  public:
    typedef void (*Func)(Type);
    FuncClosure1(Func f, Type arg, bool self_del = false)
        : f_(f), self_del_(self_del), arg_(arg) {
      CHECK_NOTNULL(f_);
    }

    ~FuncClosure1() {
      if (self_del_) {
        delete this;
      }
    }

    void Run() {
      f_(arg_);
    }

  private:
    Func f_;
    bool self_del_;
    Type arg_;

    DISALLOW_COPY_AND_ASSIGN(FuncClosure1);
};

template<typename Arg1, typename Arg2>
class FuncClosure2 : public Closure {
  public:
    typedef void (*Func)(Arg1 a1, Arg2 a2);
    FuncClosure2(Func f, Arg1 arg1, Arg2 arg2, bool self_del = false)
        : f_(f), arg1_(arg1), arg2_(arg2), self_del_(self_del) {
      CHECK_NOTNULL(f_);
    }

    ~FuncClosure2() {
      if (self_del_) delete this;
    }

    void Run() {
      f_(arg1_, arg2_);
    }

  private:
    Func f_;
    Arg1 arg1_;
    Arg2 arg2_;
    bool self_del_;

    DISALLOW_COPY_AND_ASSIGN(FuncClosure2);
};

template<typename Class>
class MethordClosure0 : public Closure {
  public:
    typedef void (Class::*Methord)();
    MethordClosure0(Class* obj, Methord m, bool self_delete = false)
        : obj_(obj), m_(m), self_del_(self_delete) {
      CHECK_NOTNULL(obj);
    }
    ~MethordClosure0() {
      if (self_del_) {
        delete this;
      }
    }

    void Run() {
      (obj_->*m_)();
    }

  private:
    Class* obj_;
    Methord m_;
    bool self_del_;

    DISALLOW_COPY_AND_ASSIGN(MethordClosure0);
};

template<typename Class, typename Arg>
class MethordClosure1 : public Closure {
  public:
    typedef void (Class::*Methord)(Arg);
    MethordClosure1(Class* obj, Methord m, Arg arg, bool self_delete = false)
        : obj_(obj), m_(m), arg_(arg), self_del_(self_delete) {
    }
    ~MethordClosure1() {
      if (self_del_) {
        delete this;
      }
    }

    virtual void Run() {
      (obj_->*m_)(arg_);
    }

  private:
    Class* obj_;
    Methord m_;
    Arg arg_;
    bool self_del_;

    DISALLOW_COPY_AND_ASSIGN(MethordClosure1);
};

Closure* NewCallback(FuncClosure0::Func f) {
  return new FuncClosure0(f, true);
}

Closure* NewPermanentCallback(FuncClosure0::Func f) {
  return new FuncClosure0(f);
}

template<typename Arg>
Closure* NewCallback(FuncClosure1<Arg>::Func1 fun, Arg arg) {
  return new FuncClosure1<Arg>(fun, arg, true);
}

template<typename Arg>
Closure* NewPermanentCallback(FuncClosure1<Arg>::Func1 fun, Arg arg) {
  return new FuncClosure1<Arg>(fun, arg);
}

template<typename Arg1, typename Arg2>
Closure* NewCallback(FuncClosure2<Arg1, Arg2>::Func2 fun, Arg1 arg1,
                     Arg2 arg2) {
  return new FuncClosure2<Arg1, Arg2>(fun, arg1, arg2, true);
}

template<typename Arg1, typename Arg2>
Closure* NewPermanentCallback(FuncClosure2<Arg1, Arg2>::Func2 fun, Arg1 arg1,
                              Arg2 arg2) {
  return new FuncClosure2<Arg1, Arg2>(fun, arg1, arg2);
}

template<typename Class>
Closure* NewCallback(Class * obj, typename MethordClosure0<Class>::Methord m) {
  return new MethordClosure0<Class>(obj, m, true);
}

template<typename Class>
Closure* NewPermanentCallback(Class* obj,
                              typename MethordClosure0<Class>::Methord m) {
  return new MethordClosure0<Class>(obj, m, false);
}

template<typename Class, typename Arg>
Closure* NewCallback(Class * obj,
                     typename MethordClosure1<Class, Arg>::Methord m, Arg arg) {
  return new MethordClosure1<Class, Arg>(obj, m, arg, true);
}

template<typename Class, typename Arg>
Closure* NewPermanentCallback(Class * obj,
                              typename MethordClosure1<Class, Arg>::Methord m,
                              Arg arg) {
  return new MethordClosure1<Class, Arg>(obj, m, arg, false);
}

#endif
